import java.io.PrintWriter;
import java.net.ServerSocket;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.Vector;

import jssc.SerialPort;
import jssc.SerialPortException;

//** Classe principale du serveur, gère les infos globales **	
public class Serveur {

	/*
	 * Structure des vélos
	 */
	private Map<Integer, EntiteVelo> velos = new HashMap<Integer, EntiteVelo>();

	/*
	 * Structure des clients
	 */
	private Map<Integer, EntiteClient> clients = new HashMap<Integer, EntiteClient>();

	private Vector<PrintWriter> _tabClients = new Vector<PrintWriter>(); // contiendra
																			// tous
																			// les
																			// flux
																			// de
	// sortie vers les clients
	private int _nbClients = 0; // nombre total de clients connectés

	//serial port arduino
	private SerialPort serialPort = new SerialPort("COM4");
	
	// ** Methode : la première méthode exécutée, elle attend les connections **
	public static void main(String args[]) throws SerialPortException {

		Serveur serv = new Serveur(); // instance de la classe
										// principale

		//serial port init for arduino
		System.out.println("Port opened: " + serv.getSerialPort().openPort());
        System.out.println("Params setted: " + serv.getSerialPort().setParams(9600, 8, 1, 0));

		
		
		Intialiser(serv);		
		try {
			Integer port;
			if (args.length <= 0)
				port = new Integer("18000"); // si pas d'argument : port 18000
												// par défaut
			else
				port = new Integer(args[0]); // sinon il s'agit du numéro de
												// port passé en argument

			new Commandes(serv); // lance le thread de gestion des
									// commandes
			
			// Lance le thread du velo simulé
			new VeloThread(serv);

			ServerSocket ss = new ServerSocket(port.intValue()); // ouverture
																	// d'un
																	// socket
																	// serveur
																	// sur port
			printWelcome(port);
			while (true) // attente en boucle de connexion (bloquant sur
							// ss.accept)
			{
				new ClientThread(ss.accept(), serv); // un client se
														// connecte, un
														// nouveau thread
														// client est lancé
			}

		} catch (Exception e) {

		}
	}

	private static void Intialiser(Serveur serv) {
		EntiteClient client1 = new EntiteClient(1, "malki", "123");
		serv.addClient(client1);
		
		EntiteVelo velo1 = new EntiteVelo(5, Etat.Libre, 0, 0);
		serv.addVelo(velo1);

	}

	synchronized private void addVelo(EntiteVelo velo1) {
		velos.put(velo1.getId(), velo1);		
	}

	// ** Methode : affiche le message d'accueil **
	static private void printWelcome(Integer port) {
		System.out.println("--------");
		System.out.println("Demarre sur le port : " + port.toString());
		System.out.println("--------");
		System.out.println("Quitter : tapez \"quit\"");
		System.out.println("Nombre de connectes : tapez \"total\"");
		System.out.println("--------");
	}

	// ** Methode : détruit le client no i **
	synchronized public void delClient(int i) {
		_nbClients--; // un client en moins ! snif
		if (_tabClients.elementAt(i) != null) // l'élément existe ...
		{
			_tabClients.removeElementAt(i); // ... on le supprime
		}
	}

	// ** Methode : ajoute un nouveau client dans la liste **
	synchronized public void addClient(EntiteClient client) {
		_nbClients++;
		clients.put(client.getId(), client);
	}

	/**
	 * Permet d'obtenir l'entité vélo à partir d'un id
	 * 
	 * @param idVelo
	 * @return le velo correspondant
	 */
	synchronized public EntiteVelo getVeloById(int idVelo) {
		return velos.get(idVelo);
	}

	/**
	 * Permet d'obtenir l'entité client à partir d'un id
	 * 
	 * @param idClient
	 * @return le client correspondant
	 */
	synchronized public EntiteClient getClientById(int idClient) {
		return clients.get(idClient);
	}

	// ** Methode : retourne le nombre de clients connectés **
	synchronized public int getNbClients() {
		return _nbClients; // retourne le nombre de clients connectés
	}

	synchronized public EntiteClient getClientByName(String login) {
		for (Entry<Integer, EntiteClient> entry : clients.entrySet()) {
			EntiteClient client = entry.getValue();
			if (client.getLogin().compareTo(login) ==  0) {
				return client;
			}
		}
		return null;
	}

	public SerialPort getSerialPort() {
		return serialPort;
	}

	public void setSerialPort(SerialPort serialPort) {
		this.serialPort = serialPort;
	}

}
